package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.LearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author 蒼飞
 * @since 2023-07-24
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements LearningLessonService {
    final CourseClient courseClient;
    final CatalogueClient catalogueClient;

    final LearningRecordMapper learningRecordMapper;
    /**
     * 添加课程
     * @param userId
     * @param courseId
     */
    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseId) {
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseId);
        if(CollUtils.isEmpty(courseInfoList)){
            log.error("课程信息为空,无法添加到课表");
            return;
        }
        List<LearningLesson> learningLessonList = new ArrayList<>(courseInfoList.size());
        //遍历处理课程信息
        for(CourseSimpleInfoDTO courseSimpleInfoDTO:courseInfoList){
            //获取课程过期时间
            Integer validDuration = courseSimpleInfoDTO.getValidDuration();
            LearningLesson learningLesson = new LearningLesson();
            if(validDuration != null && validDuration > 0){
                LocalDateTime now = LocalDateTime.now();
                //设置创建时间
                learningLesson.setCreateTime(now);
                //设置过期时间
                learningLesson.setExpireTime(now.plusMonths(validDuration));
            }
            learningLesson.setUserId(userId);
            learningLesson.setCourseId(courseSimpleInfoDTO.getId());
            learningLessonList.add(learningLesson);
        }
        //保存课程
        this.saveBatch(learningLessonList);
    }

    /**
     * 分页查询我的课程
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query){
        //1.获取当前登录人
        Long userId = UserContext.getUser();
        if(userId == null){
            throw new BadRequestException("您没有登录");
        }
        //2.分页查询我的课表
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                //如果前端没有传排序字段，则使用该字段进行排序
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if(CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //3.远程调用课程服务，给vo中的课程名，封面，章节数赋值
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        List<CourseSimpleInfoDTO> courseSimpleInfoDTO = courseClient.getSimpleInfoList(courseIds);
        if(CollUtils.isEmpty(courseSimpleInfoDTO)){
            throw new BizIllegalException("课程不存在");
        }
        //将课程集合转换成map结构
        Map<Long,CourseSimpleInfoDTO> infoDTOMap = courseSimpleInfoDTO.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId,CourseSimpleInfoDTO->CourseSimpleInfoDTO));
        //4.将po中的数据封装到vo中
        List<LearningLessonVO> voList = new ArrayList<>();
        for(LearningLesson record:records){
            //将record复制到vo
//            LearningLessonVO vo = new LearningLessonVO();
//            BeanUtils.copyProperties(record,vo);
            LearningLessonVO vo = BeanUtils.copyBean(record, LearningLessonVO.class);
            CourseSimpleInfoDTO infoDTO = infoDTOMap.get(record.getCourseId());
            if(infoDTO != null){
                vo.setCourseName(infoDTO.getName());
                vo.setCourseCoverUrl(infoDTO.getCoverUrl());
                vo.setSections(infoDTO.getSectionNum());
            }
            voList.add(vo);
        }
        //5.返回
        return PageDTO.of(page,voList);
    }

    /**
     * 查询当前正在学习的课程
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        //1.获取当前登陆用户id
        Long userId = UserContext.getUser();
        //2.查询当前用户最近学习课程 按latest_learn_time降序排序 取第一条 正在学习中的 status = 1
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId,userId)
                .eq(LearningLesson::getStatus,LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if(lesson == null){
            return null;
        }
        //3.远程调用课程服务，给vo中的课程名 封面 章节数赋值
        CourseFullInfoDTO cinfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if(cinfo == null){
            throw new BizIllegalException("课程不存在");
        }
        //4.查询当前用户课表中总的课程数
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();
        //5.通过feign.远程调用课程服务 获取小结名称 和小节编号
        Long latestSectionId = lesson.getLatestSectionId();
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(latestSectionId));
        if (cataSimpleInfoDTOS == null){
            throw new BizIllegalException("小节不存在");
        }
        //6.封装到vo返回
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        vo.setCourseName(cinfo.getName());
        vo.setCourseCoverUrl(cinfo.getCoverUrl());
        vo.setSections(cinfo.getSectionNum());
        vo.setCourseAmount(count);  //当前用户学习课程数
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOS.get(0);
        vo.setLatestSectionName(cataSimpleInfoDTO.getName());//最近学习小节名称
        vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());//最近学习小节序号
        return vo;
    }

    /**
     * 判断当前用户课程表是否有该课程
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        //1.获取用户
        Long userId = UserContext.getUser();
        //2.获取课程
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId,userId)
                .eq(LearningLesson::getCourseId,courseId)
                .one();//获取一条记录
        //3.判断用户课程表是否有该课程 如果为空说明没有记录
        if(lesson == null){
            return null;
        }
        //4.课程有效期判断
        LocalDateTime expireTime = lesson.getExpireTime();
        LocalDateTime now = LocalDateTime.now();
        int compareTo = now.compareTo(expireTime);//小于0代表没过期
        return  compareTo <= 0 ? lesson.getCourseId() : null;
    }

    /**
     * 查询用户课表中指定课程状态
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO querySpecifiedCourse(Long courseId) {
        //1.获取用户
        Long userId = UserContext.getUser();
        //2.查询课程
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId,userId)
                .eq(LearningLesson::getCourseId,courseId)
                .one();//获取一条记录
        //3.判断课程表是否有该课程
        if(lesson == null){
            return null;
        }
        //4.封装VO
        LearningLessonVO learningLessonVO = new LearningLessonVO();
        learningLessonVO.setId(lesson.getId());
        learningLessonVO.setCourseId(lesson.getCourseId());
        learningLessonVO.setStatus(lesson.getStatus());
        learningLessonVO.setLearnedSections(lesson.getLearnedSections());
        learningLessonVO.setCreateTime(lesson.getCreateTime());
        learningLessonVO.setExpireTime(lesson.getExpireTime());
        learningLessonVO.setPlanStatus(lesson.getPlanStatus());
        return learningLessonVO;
    }

    /**
     * 统计课程的学习人数
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        Integer count = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .count();
        return count;
    }

    /**
     * 创建学习计划
     * @param dto
     */
    @Override
    public void createLearningPlan(LearningPlanDTO dto) {
        //1.获取当前登陆用id
        Long userId = UserContext.getUser();
        //2.查询课表Learning_Lesson
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId,userId)
                .eq(LearningLesson::getCourseId,dto.getCourseId())
                .one();
        if(lesson == null){
            throw new BizIllegalException("该课程没有加入课表");
        }
        //3.修改课表
        //lesson.setWeekFreq(dto.getFreq());
        //this.updateById(lesson)
        //链式编程
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq,dto.getFreq())
                .set(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId,lesson.getId())
                .update();
    }

    /**
     * 查询本周学习计划
     * @param query
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //todo 2.查询积分

        //3.查询本周学习计划总数据 learning_lesson 条件userId status in(0,1) plan_status=1 ,查询sum(week_freq)

        // 方法1 mybatis查询
        //  Integer num = learningLessonMapper.queryCurrentWeekAllLearningNum(userId);

        //方法2 mybatis-plus
        QueryWrapper<LearningLesson> wrapper = new QueryWrapper<>();
        wrapper.select("SUM(week_freq) as num");//查询哪些列
        wrapper.eq("user_id",userId);
        wrapper.eq("plan_status",PlanStatus.PLAN_RUNNING);//计划正在进行
        wrapper.in("status",LessonStatus.NOT_BEGIN,LessonStatus.LEARNING);//课程未开始学习和正在学习
        Map<String,Object> map = this.getMap(wrapper);
        Integer weekTotalPlan = 0;
        if (map != null && map.get("num") != null){
            weekTotalPlan = Integer.valueOf(map.get("num").toString());
        }
        //4.查询本周已学习的计划总数
        //SELECT COUNT(*) FROM learning_record
        //WHERE user_id = 2
        //AND finished = 1
        //AND finish_time BETWEEN '2022-07-19 10:45:55' AND '2023-07-19 10:45:55';
        //获取本周起始时间
        LocalDate now = LocalDate.now();
        LocalDateTime begin = DateUtils.getWeekBeginTime(now);
        LocalDateTime end = DateUtils.getWeekEndTime(now);
        Integer weekFinished = learningRecordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .between(LearningRecord::getFinishTime, begin, end));
        //5.查询课表数据
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)//计划进行中
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if(CollUtils.isEmpty(records)){//如果查出课表数据为空，则返回空数据
            LearningPlanPageVO vo = new LearningPlanPageVO();
            vo.setTotal(0L);
            vo.setPages(0L);
            vo.setList(new ArrayList<>());
        }
        //6.远程调用课程服务 获取课程信息
        List<Long> ids = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(ids);
        Map<Long, CourseSimpleInfoDTO> cInfoMap = simpleInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        if(CollUtils.isEmpty(simpleInfoList)){
            throw new BizIllegalException("课程不存在");
        }
        //7.本周当前用户下每一门课下已学习的小节数量
//        SELECT lesson_id,COUNT(*) FROM learning_record
//        WHERE user_id = 2
//        AND finished = 1
//        AND finish_time BETWEEN '2021-10-19 10:45:55' AND '2023-10-19 10:45:55'
//        GROUP BY lesson_id
        QueryWrapper<LearningRecord> learningRecordQueryWrapper = new QueryWrapper<>();
        learningRecordQueryWrapper.select("lesson_id as lessonId,count(*) as moment");
        learningRecordQueryWrapper.eq("user_id",userId);
        learningRecordQueryWrapper.eq("finished", true);
        learningRecordQueryWrapper.between("finish_time",begin,end);
        learningRecordQueryWrapper.groupBy("lesson_id");
        List<LearningRecord> learningRecords = learningRecordMapper.selectList(learningRecordQueryWrapper);
        Map<Long, Integer> maps = learningRecords.stream().collect(Collectors.toMap(LearningRecord::getLessonId, LearningRecord::getMoment));

        //8.封装vo返回
        LearningPlanPageVO learningPlanPageVO = new LearningPlanPageVO();
        learningPlanPageVO.setWeekFinished(weekFinished);
        learningPlanPageVO.setWeekTotalPlan(weekTotalPlan);
        learningPlanPageVO.setWeekPoints(999);
        List<LearningPlanVO> list = new ArrayList<>();
        for(LearningLesson record:records){
            LearningPlanVO vo = BeanUtils.copyBean(record,LearningPlanVO.class);
            CourseSimpleInfoDTO infoDTO = cInfoMap.get(record.getCourseId());
            if(infoDTO != null){
                vo.setCourseName(infoDTO.getName());
                vo.setSections(infoDTO.getSectionNum());
            }
            //如果map为空说明该课程没有学习记录 则置为0
            vo.setWeekLearnedSections(CollUtils.isEmpty(maps) ? 0 : maps.get(record.getId()));
            list.add(vo);
        }
        learningPlanPageVO.setList(list);
        learningPlanPageVO.setTotal(page.getTotal());
        learningPlanPageVO.setPages(page.getPages());
        return learningPlanPageVO;
    }
}
